﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace QQ.Observer
{
    public class Q3
    {
        public class Location
        {
            public Location(double left, double right)
            {
                this._left = left;
                this._right = right;
            }

            public double Left
            {
                get { return _left; }
                set { _left = value; }
            }

            public double Right
            {
                get { return _right; }
                set { _right = value; }
            }

            double _left, _right;

        }
        public class Observer : IObserver<Location>
        {
            public void OnNext(Location value)
            {
                throw new NotImplementedException();
            }

            public void OnError(Exception error)
            {
                throw new NotImplementedException();
            }

            public void OnCompleted()
            {
                throw new NotImplementedException();
            }
        }

        public class LocationTracker : IObservable<Location>
        {
            private List<IObserver<Location>> _observers;

            public LocationTracker()
            {
                _observers = new List<IObserver<Location>>();
            }
            public LocationTracker(List<IObserver<Location>> observers)
            {
                _observers = observers;
            }

            public IDisposable Subscribe(IObserver<Location> observer)
            {
                if (this._observers.Contains(observer) == false)
                {
                    this._observers.Add(observer);
                }
                return new Unsubscriber(_observers, observer);
            }

            private class Unsubscriber : IDisposable
            {
                private List<IObserver<Location>> _observers;
                private IObserver<Location> _observer;

                public Unsubscriber(List<IObserver<Location>> observers, IObserver<Location> observer)
                {
                    _observer = observer;
                    _observers = observers;
                }

                public void Dispose()
                {
                    if (_observer != null && _observers.Contains(_observer))
                    {
                        _observers.Remove(_observer);
                    }
                }
            }

            public void TrackLocation(Location loc)
            {
                foreach (var observer in this._observers)
                {
                    if (loc != null)
                    {
                        observer.OnNext(loc);
                    }
                    else
                    {
                        observer.OnError(new LocationUnknownException());

                    }
                }
            }

            public void EndTransmission()
            {
                foreach (var observer in this._observers)
                {
                    if (this._observers.Contains(observer))
                    {
                        observer.OnCompleted();

                    }

                }
                this._observers.Clear();
            }
        }

        public class LocationUnknownException : Exception
        {
            internal LocationUnknownException()
            {

            }
        }

        public class LocationReporter : IObserver<Location>
        {
            private IDisposable unsubscriber;
            private string instName;

            public LocationReporter(string name)
            {
                this.instName = name;
            }

            public string Name
            { get { return this.instName; } }

            public virtual void Subscribe(IObservable<Location> provider)
            {
                if (provider != null)
                    unsubscriber = provider.Subscribe(this);
            }

            public virtual void OnCompleted()
            {
                Console.WriteLine("The Location Tracker has completed transmitting data to {0}.", this.Name);
                this.Unsubscribe();
            }

            public virtual void OnError(Exception e)
            {
                Console.WriteLine("{0}: The location cannot be determined.", this.Name);
            }

            public virtual void OnNext(Location value)
            {
                Console.WriteLine("{2}: The current location is {0}, {1}", value.Left, value.Right, this.Name);
            }

            public virtual void Unsubscribe()
            {
                unsubscriber.Dispose();
            }
        }

        public static void QMain()
        {
            // Define a provider and two observers.
            LocationTracker provider = new LocationTracker();
            LocationReporter reporter1 = new LocationReporter("FixedGPS");
            reporter1.Subscribe(provider);
            LocationReporter reporter2 = new LocationReporter("MobileGPS");
            reporter2.Subscribe(provider);

            provider.TrackLocation(new Location(47.6456, -122.1312));
            reporter1.Unsubscribe();
            provider.TrackLocation(new Location(47.6677, -122.1199));
            provider.TrackLocation(null);
            provider.EndTransmission();

            Console.ReadKey();
        }

        // The example displays output similar to the following:
        //      FixedGPS: The current location is 47.6456, -122.1312
        //      MobileGPS: The current location is 47.6456, -122.1312
        //      MobileGPS: The current location is 47.6677, -122.1199
        //      MobileGPS: The location cannot be determined.
        //      The Location Tracker has completed transmitting data to MobileGPS.
    }
}
